package com.wang.test.demo.test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyData{
    /**
     * volatile 修饰的关键字，是为了增加 主线程和线程之间的可见性，只要有一个线程修改了内存中的值，其它线程也能马上感知
     */
    volatile int num = 0;

    AtomicInteger atomicInteger = new AtomicInteger();

    public  void add(){
        this.num = 990;
    }
    //不保证原子性
    public void addPlus(){
        num++;
    }

    public void addPlusPlus(){
        atomicInteger.getAndIncrement();//每次加1
    }
}
/**
 * 1.验证volatile的可见性
 *      1.1 假设int number = 0， number变量之前没有添加volatile关键字修饰
 *      1.2 添加volatile,可以解决可见性问题
 * 2.验证volatile不保证原子性
 *      2.1原子性指的是什么意思？
 *          不可分割，完整性，也即某个线程正在做某个具体业务时，中间不可以被加塞或者被分割。需要整体完整要么同时成功，要么同时失败。
 *      2.2不保证原子性
 */
public class VolatileDemo {

    public static void main(String[] args) {

        MyData myData = new MyData();
        for (int x = 0; x < 20;x++){
            new Thread(() -> {
                for (int y = 0;y < 1000 ;y++){
                    myData.addPlus();
                    myData.addPlusPlus();
                }
            },String.valueOf(x)).start();

        }

        // 需要等待上面20个线程都计算完成后，在用main线程取得最终的结果值
        // 这里判断线程数是否大于2，为什么是2？因为默认是有两个线程的，一个main线程，一个gc线程
        while(Thread.activeCount() > 2) {
            // yield表示不执行
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"\t finally number value " + myData.num);
        System.out.println(Thread.currentThread().getName()+"\t finally number value " + myData.atomicInteger);

    }

    /**
     * volatile=保证可见性,及时通知其他线程,主物理内存的值已经被修改
     */
    public static  void seeOkByVolatile(){
        MyData myData = new MyData();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.add();
            System.out.println(Thread.currentThread().getName()+"\t update number value : " + myData.num);
        },"AAA").start();

        // main线程就一直在这里等待循环，直到number的值不等于零
        while(myData.num == 0) {}

        // 按道理这个值是不可能打印出来的，因为主线程运行的时候，number的值为0，所以一直在循环
        // 如果能输出这句话，说明AAA线程在睡眠3秒后，更新的number的值，重新写入到主内存，并被main线程感知到了
        System.out.println(Thread.currentThread().getName() + "\t mission is over");
    }
}
